/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "FusionStorageApi.h"
#include <system/System.hpp>
#include <algorithm>
#include "log/Log.h"
#include "config_reader/ConfigIniReader.h"
#include "volume_handlers/oceanstor/DiskScannerHandler.h"
#include "tracepoint/EbkTracePoint.h"
#include "config_reader/ConfigIniReaderImpl.h"
#include "common/utils/Utils.h"

namespace {
const int32_t BITMAP_VALUME_STATUS_CREATING = 7;
const std::string MODULE_NAME = "FusionStorageApi";
const int32_t RETRY_INTERVAL = 30;
const int32_t QUERY_BITMAP_CREATING_INTERVAL = 10;
const int32_t SLEEP_TIME_AFTER_ATTACHED = 5;
std::string dsAgentIp = "127.0.0.1";
const std::string VBS_RET_CODE_KEY = "ret_code";
const std::string VBS_RET_DESC_KEY = "ret_desc";
const std::string VBS_DEV_PATH_KEY = "dev_addr";
const std::string VBS_TOOL_RET_CODE_KEY = "ResultCode";
const std::string VBS_TOOL_SNAP_SIZE_KEY = "snapSize";
const std::string VBS_TOOL_BLOCK_SIZE_KEY = "blockSize";
const std::string VBS_TOOL_VOLUME_SIZE_KEY = "volSize";
const std::string VBS_TOOL_STATUS_KEY = "status";
const std::string VBS_TOOL_BITMAP_SIZE_KEY = "bitmapSize";
const std::string VBS_TOOL_TOTAL_CAPACITY_SIZE_KEY = "totalCapacity";
const std::string VBS_TOOL_USED_CAPACITY_SIZE_KEY = "usedCapcity";
const std::string SUDO_VBSTOOL_PATH = VirtPlugin::VIRT_PLUGIN_PATH + "vbstool/vrmVBSTool.sh";
const std::string SUDO_VBSCLI_PATH = VirtPlugin::VIRT_PLUGIN_PATH + "bin/security_sudo_vbs_cli.sh";

const int32_t CONSTANT_NUM_10 = 10;
const int32_t RETRY_TIMES = 3;
const int32_t MAX_QUERY_TIMES = 5;
const int32_t MAX_EXPAND_TIMES = 5;
}  // namespace

VIRT_PLUGIN_NAMESPACE_BEGIN

FusionStorageApi::FusionStorageApi(const std::string &fusionStorMgrIp, const std::string &poolID)
    : m_fusionStorMgrIp(fusionStorMgrIp), m_poolID(poolID)
{
    m_codeToErrorDes[DSWARE_API_OK] = "OK";
    m_codeToErrorDes[DSWARE_API_ERR_DSWARE_AGENT_NOT_INSTALL] = "DSware agent is not be installed.";
    m_codeToErrorDes[DSWARE_API_ERR_JAVA_FILE_IS_NOT_EXIST] = "Java file is not exist.";
    m_codeToErrorDes[DSWARE_API_ERR_JAVA_FILE_HAVE_NO_PERMISSION] = "Java file have no permission.";
    m_codeToErrorDes[DSWARE_API_ERR_VBSTOOL_CONF_FAULT] = "VBS_TOOL configure fault.";
    m_codeToErrorDes[DSWARE_API_ERR_SNAP_IS_NOT_EXIST] = "The snapshot is not exist.";
    m_codeToErrorDes[DSWARE_API_ERR_VOLUME_IS_NOT_EXIST] = "The volume is not exitst.";
    m_codeToErrorDes[DSWARE_API_ERR_VOLUME_HAS_BEEN_ATTACH] = "The volume has been attached.";
    m_codeToErrorDes[DSWARE_API_ERR_VOLUME_HAS_BEEN_DETACH] = "The volume has been detached.";
    m_codeToErrorDes[DSWARE_API_ERR_VOLUME_OR_SNAPSHOT_IS_NOT_EXIST] = "The volume or snapshot ist not exist.";
    m_codeToErrorDes[DSWARE_API_ERR_CREATE_BITMAP_VOL_SNAP_BRANCH_ERR] =
        "The from snap and to volume of bitmap volume are different branch.";
    m_codeToErrorDes[DSWARE_API_ERR_CREATE_BITMAP_SNAPSHOT_REPEATED_ERR] =
        "The snapshotID is same with previous backup.";
    m_codeToErrorDes[DSWARE_API_ERR_UNKNOWN] = "Unknown error.";
}

FusionStorageApi::~FusionStorageApi()
{}

void FusionStorageApi::GetDswareAgentIp(const std::string &dswareMgrIp)
{
    if (dswareMgrIp.find_last_of(':') != std::string::npos) {
        dsAgentIp = "::1";
    } else {
        dsAgentIp = "127.0.0.1";
    }
}

int32_t FusionStorageApi::CreateBitmapVolume(const std::shared_ptr<RepositoryHandler> cacheRepoHandler,
    const std::string &filePath, BitmapVolumeInfo &info, std::string &errDes)
{
    DBGLOG("Start create bitmap volume.");

    int32_t iRet = DoCreateBitmapVol(info, errDes);
    DBGLOG("CreateBitmapVolume end.");
    if (iRet != DSWARE_API_OK) {
        ERRLOG("CODE[%d] create bitmap failed. Error string: %s.", iRet, errDes.c_str());
        return iRet;
    }
    std::string infoStr;
    Module::JsonHelper::StructToJsonString(info, infoStr);
    if (Utils::SaveToFileWithRetry(cacheRepoHandler, filePath, infoStr) != SUCCESS) {
        ERRLOG("Save bitmap volume %s info to %s failed.", info.volName.c_str(), filePath.c_str());
        return FAILED;
    }
    INFOLOG("Save bitmap volume %s info to %s success.", info.volName.c_str(), filePath.c_str());
    if (RetryToQueryBitmap(info) != SUCCESS) {
        ERRLOG("RetryToQueryBitmap failed.");
        return FAILED;
    }
    INFOLOG("Create bitmap volume %s success.", info.volName.c_str());
    return SUCCESS;
}

int32_t FusionStorageApi::RetryToQueryBitmap(BitmapVolumeInfo &info)
{
    BitmapVolumeInfo temp;
    temp.volName = info.volName;
    int32_t queryFailedTime = 0;
    int32_t expandTime = 0;
    int32_t createBitmapVolumeTimeOut = Module::ConfigReader::getInt(
        std::string("HcsConfig"), std::string("CreateBitmapVolumeTimeOut"));
    auto startTime = std::chrono::steady_clock::now();
    createBitmapVolumeTimeOut = std::max(createBitmapVolumeTimeOut, info.createTimeOut);
    INFOLOG("CreateBitmapVolume timeout is %d.", createBitmapVolumeTimeOut);
    std::string errDes;
    while (true) {
        if (!ExpandWaitTime(startTime, expandTime, temp, createBitmapVolumeTimeOut)) {
            ERRLOG("Waiting CreateBitmapVolume result timeout, timeOut: %d.", createBitmapVolumeTimeOut);
            return DSWARE_API_ERR_UNKNOWN;
        }

        DBGLOG("Query create bitmap volume status.");
        int32_t errorCode = QueryBitmapVol(temp, errDes);
        if (errorCode != DSWARE_API_OK) {
            ERRLOG("CODE[%d] query bitmap failed.", errorCode);
            queryFailedTime++;
            if (queryFailedTime <= MAX_QUERY_TIMES) {
                std::this_thread::sleep_for(std::chrono::seconds(QUERY_BITMAP_CREATING_INTERVAL));
                continue;
            }
            return errorCode;
        }

        queryFailedTime = 0;
        DBGLOG("Bitmap volume create status:[%d].", temp.status);
        if (temp.status == 0) {
            return DSWARE_API_OK;
        }

        if (temp.status != BITMAP_VALUME_STATUS_CREATING) {
            errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
            return DSWARE_API_ERR_UNKNOWN;
        }

        std::this_thread::sleep_for(std::chrono::seconds(QUERY_BITMAP_CREATING_INTERVAL));
    }
    return DSWARE_API_OK;
}

bool FusionStorageApi::ExpandWaitTime(std::chrono::steady_clock::time_point &startTime, int32_t &expandTime,
    const BitmapVolumeInfo &temp, const int32_t &createBitmapVolumeTimeOut)
{
    if ((uint64_t(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() -
        startTime).count()) * std::chrono::microseconds::period::num / std::chrono::microseconds::period::den) <
        createBitmapVolumeTimeOut) {
        WARNLOG("CreateBitmapVolume not timeout, continue to wait.");
        return true;
    }
    if (temp.status != BITMAP_VALUME_STATUS_CREATING) {
        WARNLOG("CreateBitmapVolume not in creating status, continue to wait.");
        return true;
    }
    if (expandTime < MAX_EXPAND_TIMES) {
        ++expandTime;
        startTime = std::chrono::steady_clock::now();
        INFOLOG(
            "CreateBitmapVolume timeout, start to expand wait time, current start time: %lld, current expand time: %d.",
            uint64_t(startTime.time_since_epoch().count() *
            std::chrono::microseconds::period::num / std::chrono::microseconds::period::den), expandTime);
        return true;
    }
    return false;
}
int32_t FusionStorageApi::DoCreateBitmapVol(BitmapVolumeInfo &info, std::string &errDes)
{
    GetDswareAgentIp(m_fusionStorMgrIp);
    std::vector<Module::CmdParam> cmdParam;
    SetCreateBitmapCmdParam(cmdParam, info);

    std::vector<std::string> resultvec;
    int32_t runResult = Utils::CallAgentExecCmd(cmdParam, resultvec);
    if (runResult != 0) {
        ERRLOG("Create different bitmap volume: %s failed, errDesc: %s.",
            info.volName.c_str(), VectorToStr(resultvec).c_str());
    } else {
        INFOLOG("Create different bitmap volume %s successfully.", info.volName.c_str());
    }

    std::map<std::string, std::string> values = ToKevValue(resultvec);
    bool bCheck = values.find(VBS_TOOL_RET_CODE_KEY) != values.end();
    if (!bCheck) {
        ERRLOG("Create bitmap volume failed, retrun value error.");
        errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
        return DSWARE_API_ERR_UNKNOWN;
    }
    int32_t returnCode = atoi(values[VBS_TOOL_RET_CODE_KEY].c_str());
    if (returnCode != 0) {
        ERRLOG("Create bitmap volume failed, return code: %d, error string: %s",
            returnCode, GetErrString(returnCode).c_str());
        errDes = GetErrString(returnCode);
        return returnCode;
    }

    bCheck = values.find(VBS_TOOL_SNAP_SIZE_KEY) != values.end() &&
             values.find(VBS_TOOL_BLOCK_SIZE_KEY) != values.end() &&
             values.find(VBS_TOOL_BITMAP_SIZE_KEY) != values.end();
    if (!bCheck) {
        ERRLOG("Create bitmap volume failed, retrun value error.");
        errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
        return DSWARE_API_ERR_UNKNOWN;
    }

    info.snapSize = atoi(values[VBS_TOOL_SNAP_SIZE_KEY].c_str());
    info.blockSize = atoi(values[VBS_TOOL_BLOCK_SIZE_KEY].c_str());
    info.volSize = atoi(values[VBS_TOOL_BITMAP_SIZE_KEY].c_str());

    return DSWARE_API_OK;
}

void FusionStorageApi::SetCreateBitmapCmdParam(std::vector<Module::CmdParam> &cmdParam, const BitmapVolumeInfo &info)
{
    std::string agentHomedir = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    cmdParam = {
        Module::CmdParam(Module::COMMON_CMD_NAME, "sudo"),
        Module::CmdParam(Module::SCRIPT_CMD_NAME, agentHomedir + SUDO_VBSTOOL_PATH),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--op"),
        "createBitmapVolume",
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--dsaIp"),
        Module::CmdParam(Module::COMMON_PARAM, dsAgentIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--snapNameFrom"),
        Module::CmdParam(Module::COMMON_PARAM, info.snapNameFrom),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--snapNameTo"),
        Module::CmdParam(Module::COMMON_PARAM, info.snapNameTo),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--dswareFloatIP"),
        Module::CmdParam(Module::COMMON_PARAM, m_fusionStorMgrIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--volName"),
        Module::CmdParam(Module::COMMON_PARAM, info.volName),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--poolId"),
        Module::CmdParam(Module::COMMON_PARAM, m_poolID)
    };
}

int32_t FusionStorageApi::QueryBitmapVol(BitmapVolumeInfo &info, std::string &errDes)
{
    GetDswareAgentIp(m_fusionStorMgrIp);
    INFOLOG("Start to query volume bitmap, volume name: %s, dswareMgr IP: %s, dsAgent IP: %s, pool ID: %s",
        info.volName.c_str(), m_fusionStorMgrIp.c_str(), dsAgentIp.c_str(), m_poolID.c_str());
    std::vector<Module::CmdParam> cmdParam;
    SetQueryBitmapCmdParam(cmdParam, info);

    std::vector<std::string> resultvec;
    int32_t runResult = Utils::CallAgentExecCmd(cmdParam, resultvec);
    if (runResult != 0) {
        ERRLOG("Query different bitmap volume %s failed.", info.volName.c_str());
    } else {
        INFOLOG("Query different bitmap volume %s successfully.", info.volName.c_str());
    }

    std::map<std::string, std::string> values = ToKevValue(resultvec);
    bool bCheck = values.find(VBS_TOOL_RET_CODE_KEY) != values.end();
    if (!bCheck) {
        ERRLOG("Query bitmap volume failed, retrun value error.");
        errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
        return DSWARE_API_ERR_UNKNOWN;
    }

    int32_t returnCode = atoi(values[VBS_TOOL_RET_CODE_KEY].c_str());
    if (returnCode != 0) {
        ERRLOG("Query bitmap volume failed, return code: %d, error string: %s.",
            returnCode, GetErrString(returnCode).c_str());
        errDes = GetErrString(returnCode);
        return returnCode;
    }

    bCheck = values.find(VBS_TOOL_SNAP_SIZE_KEY) != values.end() &&
             values.find(VBS_TOOL_BLOCK_SIZE_KEY) != values.end() &&
             values.find(VBS_TOOL_VOLUME_SIZE_KEY) != values.end() && values.find(VBS_TOOL_STATUS_KEY) != values.end();
    if (!bCheck) {
        ERRLOG("Query bitmap volume failed, retrun value error.");
        errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
        return DSWARE_API_ERR_UNKNOWN;
    }

    info.snapSize = atoi(values[VBS_TOOL_SNAP_SIZE_KEY].c_str());
    info.blockSize = atoi(values[VBS_TOOL_BLOCK_SIZE_KEY].c_str());
    info.volSize = atoi(values[VBS_TOOL_VOLUME_SIZE_KEY].c_str());
    info.status = atoi(values[VBS_TOOL_STATUS_KEY].c_str());

    return DSWARE_API_OK;
}

void FusionStorageApi::SetQueryBitmapCmdParam(std::vector<Module::CmdParam> &cmdParam, const BitmapVolumeInfo &info)
{
    std::string agentHomedir = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    cmdParam = {
        Module::CmdParam(Module::COMMON_CMD_NAME, "sudo"),
        Module::CmdParam(Module::SCRIPT_CMD_NAME, agentHomedir + SUDO_VBSTOOL_PATH),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--op"),
        "queryBitmapVolume",
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--dsaIp"),
        Module::CmdParam(Module::COMMON_PARAM, dsAgentIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--dswareFloatIP"),
        Module::CmdParam(Module::COMMON_PARAM, m_fusionStorMgrIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--volName"),
        Module::CmdParam(Module::COMMON_PARAM, info.volName),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--poolId"),
        Module::CmdParam(Module::COMMON_PARAM, m_poolID)
    };
}

std::string FusionStorageApi::GetErrString(const int code)
{
    std::map<int, std::string>::iterator it = m_codeToErrorDes.find(code);
    if (it != m_codeToErrorDes.end()) {
        return it->second;
    } else {
        return "Unknown error.";
    }
}

std::map<std::string, std::string> FusionStorageApi::ToKevValue(const std::vector<std::string> &strings)
{
    std::map<std::string, std::string> values;
    for (std::vector<std::string>::const_iterator it = strings.begin(); it != strings.end(); ++it) {
        std::string::size_type pos = it->find("=");
        if (pos == std::string::npos) {
            pos = it->find(":");
            if (pos == std::string::npos) {
                continue;
            }
        }
        values[it->substr(0, pos)] = it->substr(pos + 1);
    }
    return values;
}

int32_t FusionStorageApi::DeleteBitmapVolume(const std::string &volumeName, std::string &errDes)
{
    GetDswareAgentIp(m_fusionStorMgrIp);
    INFOLOG("Start to delete volume, volume name: %s, dswareMgr IP: %s, dsAgent IP: %s",
        volumeName.c_str(), m_fusionStorMgrIp.c_str(), dsAgentIp.c_str());

    bool redoCMD = false;
    int32_t tryTimes = 0;
    int32_t returnCode = 0;

    do {
        if (redoCMD) {
            WARNLOG("Maybe dsware is busy, wait for %d to retry.", CONSTANT_NUM_10 * RETRY_INTERVAL);
            std::this_thread::sleep_for(std::chrono::seconds(CONSTANT_NUM_10 * RETRY_INTERVAL));
        }

        redoCMD = false;
        returnCode = DeleteBitmapVolumeNoRetry(volumeName, errDes);
        INFOLOG("DeleteBitmapVolume End, vol name: %s", volumeName.c_str());
        if (returnCode == DSWARE_API_OK) {
            return DSWARE_API_OK;
        }

        if (returnCode == DSWARE_API_ERR_UNKNOWN) {
            ERRLOG("Create bitmap volume failed, Error string: %s", errDes.c_str());
            return DSWARE_API_ERR_UNKNOWN;
        }

        // When clean task move to another node, it may be slow to detele previous bitmap volume
        if (returnCode == DSWARE_API_ERR_VOLUME_IS_CREATING_BITMAP_VOLUME) {
            tryTimes++;
            if (tryTimes <= RETRY_TIMES) {
                redoCMD = true;
            }
        }
    } while (redoCMD);

    ERRLOG("Delete bitmap volume failed, return code: %d, error string: %s",
        returnCode, GetErrString(returnCode).c_str());
    errDes = GetErrString(returnCode);

    return returnCode;
}

int32_t FusionStorageApi::AttachVolume(const std::string &volumeName, std::string &diskDevicePath, std::string &errDes,
    const std::shared_ptr<RepositoryHandler> cacheRepoHandler, const std::string &filePath)
{
    GetDswareAgentIp(m_fusionStorMgrIp);
    INFOLOG("Start to attach volume, volume name: %s, dsware IP: %s, poolId: %s.",
        volumeName.c_str(), m_fusionStorMgrIp.c_str(), m_poolID.c_str());

    // usr/bin/vbs_cli -c attachwithip -v [vol_name] -i [float_ip] -p [pool_id] pool_id default is 0
    std::string agentHomedir = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    std::vector<Module::CmdParam> cmdParam;
    SetAttachVolumeCmdParam(cmdParam, volumeName);

    std::vector<std::string> resultvec;
    if (Utils::CallAgentExecCmd(cmdParam, resultvec) != 0) {
        ERRLOG("Attach volume: %s.", volumeName.c_str());
    } else {
        INFOLOG("Attach volume %s successfully.", volumeName.c_str());
    }

    std::map<std::string, std::string> values = ToKevValue(resultvec);
    if (values.find(VBS_RET_CODE_KEY) == values.end()) {
        ERRLOG("Return value is not found %s.", VBS_RET_CODE_KEY.c_str());
        errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
        return DSWARE_API_ERR_UNKNOWN;
    }

    if (values.find(VBS_RET_DESC_KEY) == values.end()) {
        ERRLOG("Return value is not found %s.", VBS_RET_DESC_KEY.c_str());
        errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
        return DSWARE_API_ERR_UNKNOWN;
    }

    int32_t ret = atoi(values[VBS_RET_CODE_KEY].c_str());
    errDes = values[VBS_RET_DESC_KEY];
    if (ret == DSWARE_API_OK) {
        DBGLOG("Attach volume[%s] success.", volumeName.c_str());
        if (values.find(VBS_DEV_PATH_KEY) == values.end()) {
            ERRLOG("Return value is not found %s.", VBS_DEV_PATH_KEY.c_str());
            errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
            return DSWARE_API_ERR_UNKNOWN;
        }
        diskDevicePath = values[VBS_DEV_PATH_KEY];
        std::this_thread::sleep_for(std::chrono::seconds(SLEEP_TIME_AFTER_ATTACHED));
        UpdateVolumeAttachInfoFile(cacheRepoHandler, filePath, volumeName);
        return DSWARE_API_OK;
    }

    if (ret == DSWARE_API_ERR_VOLUME_HAS_BEEN_ATTACH) {
        diskDevicePath = values[VBS_DEV_PATH_KEY];
        DBGLOG("Volume[%s] has been attach.", volumeName.c_str());
        UpdateVolumeAttachInfoFile(cacheRepoHandler, filePath, volumeName);
        return DSWARE_API_OK;
    }

    ERRLOG("Attach volume[%s] failed. Reason is %s.", volumeName.c_str(), errDes.c_str());
    return ret;
}

int32_t FusionStorageApi::UpdateVolumeAttachInfoFile(const std::shared_ptr<RepositoryHandler> cacheRepoHandler,
    const std::string &filePath, const std::string &volumeName)
{
    std::string attachInfoFile = filePath + volumeName + "_volumeAttach.info";
    VolumeAttachInfo attachInfo;
    if (cacheRepoHandler == nullptr) {
        ERRLOG("Cache repohandle is null.");
        return FAILED;
    }
    std::string infoStr;
    if (cacheRepoHandler->Exists(attachInfoFile)) {
        Utils::ReadFile(cacheRepoHandler, attachInfoFile, infoStr);
        Module::JsonHelper::JsonStringToStruct(infoStr, attachInfo);
    }
    attachInfo.volName = volumeName;
    std::string nodeManageIp;
    if (GetNodeManageIp(nodeManageIp) == SUCCESS) {
        attachInfo.attachIpList.push_back(nodeManageIp);
    }
    Module::JsonHelper::StructToJsonString(attachInfo, infoStr);
    if (Utils::SaveToFileWithRetry(cacheRepoHandler, attachInfoFile, infoStr) != SUCCESS) {
        ERRLOG("Save volume %s attach info to %s failed.", volumeName.c_str(), filePath.c_str());
        return FAILED;
    }
    INFOLOG("Save volume %s attach info success.", volumeName.c_str());
    return SUCCESS;
}

void FusionStorageApi::SetAttachVolumeCmdParam(std::vector<Module::CmdParam> &cmdParam, const std::string &volumeName)
{
    std::string agentHomedir = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    cmdParam = {
        Module::CmdParam(Module::COMMON_CMD_NAME, "sudo"),
        Module::CmdParam(Module::SCRIPT_CMD_NAME, agentHomedir + SUDO_VBSCLI_PATH),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-c"),
        "attachwithip",
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-v"),
        Module::CmdParam(Module::COMMON_PARAM, volumeName),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-i"),
        Module::CmdParam(Module::COMMON_PARAM, m_fusionStorMgrIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-p"),
        Module::CmdParam(Module::COMMON_PARAM, m_poolID)
    };
}

int32_t FusionStorageApi::GetNodeManageIp(std::string &ip)
{
    std::vector<Module::CmdParam> cmdParam = {
        Module::CmdParam(Module::COMMON_CMD_NAME, "cat"),
        Module::CmdParam(Module::PATH_PARAM, "/opt/network/network_config.ini"),
        Module::CmdParam(Module::PIPELINE_PARAM, "|"),
        Module::CmdParam(Module::COMMON_CMD_NAME, "grep"),
        "om_ip",
        Module::CmdParam(Module::PIPELINE_PARAM, "|"),
        Module::CmdParam(Module::COMMON_CMD_NAME, "awk"),
        Module::CmdParam(Module::COMMON_PARAM, "-F"),
        "'=' '{print $2}'"
    };
    std::vector<std::string> resultvec;
    if (Utils::CallAgentExecCmd(cmdParam, resultvec) != 0 || resultvec.size() == 0) {
        ERRLOG("Get om_ip failed.");
        return FAILED;
    }
    DBGLOG("Get om_ip[%s] success.", resultvec[0].c_str());
    ip = resultvec[0];
    return SUCCESS;
}

int32_t FusionStorageApi::DetachVolume(const std::string &volumeName, std::string &errDes,
    const std::shared_ptr<RepositoryHandler> cacheRepoHandler, const std::string &filePath)
{
    DetachVolumeAllAttach(cacheRepoHandler, filePath, volumeName);
    GetDswareAgentIp(m_fusionStorMgrIp);
    INFOLOG("Start to detach volume, volume name: %s, dswareMgr IP: %s.",
        volumeName.c_str(), m_fusionStorMgrIp.c_str());
    std::vector<Module::CmdParam> cmdParam;
    SetDetachVolumeCmdParam(cmdParam, volumeName);

    std::vector<std::string> resultvec;
    if (Utils::CallAgentExecCmd(cmdParam, resultvec) != 0) {
        ERRLOG("Detach volume: %s failed.", volumeName.c_str());
    } else {
        INFOLOG("Detach volume %s successfully.", volumeName.c_str());
    }

    std::map<std::string, std::string> values = ToKevValue(resultvec);
    if (values.find(VBS_RET_CODE_KEY) == values.end()) {
        ERRLOG("Return value is not found %s.", VBS_RET_CODE_KEY.c_str());
        errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
        return DSWARE_API_ERR_UNKNOWN;
    }

    if (values.find(VBS_RET_DESC_KEY) == values.end()) {
        ERRLOG("Return value is not found %s.", VBS_RET_DESC_KEY.c_str());
        errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
        return DSWARE_API_ERR_UNKNOWN;
    }

    int32_t ret = atoi(values[VBS_RET_CODE_KEY].c_str());
    errDes = values[VBS_RET_DESC_KEY];

    if (ret == DSWARE_API_OK) {
        DBGLOG("Detach volume[%s] success.", volumeName.c_str());
        return DSWARE_API_OK;
    }

    if (ret == DSWARE_API_ERR_VOLUME_HAS_BEEN_DETACH) {
        DBGLOG("Volume[%s] has been detached.", volumeName.c_str());
        return DSWARE_API_OK;
    }
    if (ret == DSWARE_API_ERR_VOLUME_OR_SNAPSHOT_IS_NOT_EXIST) {
        INFOLOG("Volume[%s] is not exist.", volumeName.c_str());
        return DSWARE_API_OK;
    }

    ERRLOG("Detach volume[%s] failed, ret: %d, error string: %s.", volumeName.c_str(), ret, errDes.c_str());
    return ret;
}

void FusionStorageApi::SetDetachVolumeCmdParam(std::vector<Module::CmdParam> &cmdParam, const std::string &volumeName)
{
    std::string agentHomedir = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    cmdParam = {
        Module::CmdParam(Module::COMMON_CMD_NAME, "sudo"),
        Module::CmdParam(Module::SCRIPT_CMD_NAME, agentHomedir + SUDO_VBSCLI_PATH),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-c"),
        "detachwithip",
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-v"),
        Module::CmdParam(Module::COMMON_PARAM, volumeName),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-i"),
        Module::CmdParam(Module::COMMON_PARAM, m_fusionStorMgrIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-p"),
        Module::CmdParam(Module::COMMON_PARAM, m_poolID)
    };
}

void FusionStorageApi::UpdateMgrIPAndPoolID(const std::string &mgrIP, const std::string &poolID)
{
    m_fusionStorMgrIp = mgrIP;
    m_poolID = poolID;
}

std::string FusionStorageApi::GetPoolID() const
{
    return m_poolID;
}

int32_t FusionStorageApi::DeleteBitmapVolumeNoRetry(const std::string &volumeName, std::string &errDes)
{
    GetDswareAgentIp(m_fusionStorMgrIp);
    INFOLOG("Start to delete volume, volume name: %s, dswareMgr IP: %s, dsAgent IP: %s.",
        volumeName.c_str(), m_fusionStorMgrIp.c_str(), dsAgentIp.c_str());

    std::string agentHomedir = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    int ifDelete = 1;
    TP_START("TP_DeleteBitmapFaield", 1, &ifDelete);
    TP_END
    std::vector<Module::CmdParam> cmdParam {
        Module::CmdParam(Module::COMMON_CMD_NAME, "sudo"),
        Module::CmdParam(Module::SCRIPT_CMD_NAME, agentHomedir + SUDO_VBSTOOL_PATH),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--op"),
        "deleteVolume",
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--dsaIp"),
        Module::CmdParam(Module::COMMON_PARAM, dsAgentIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--dswareFloatIP"),
        Module::CmdParam(Module::COMMON_PARAM, m_fusionStorMgrIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--volName"),
        Module::CmdParam(Module::COMMON_PARAM, volumeName),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--poolId"),
        Module::CmdParam(Module::COMMON_PARAM, m_poolID)
    };

    std::vector<std::string> resultvec;
    int32_t runResult;
    if (ifDelete == 1) {
        runResult = Utils::CallAgentExecCmd(cmdParam, resultvec);
    }
    if (runResult != 0) {
        ERRLOG("Delete volume: %s failed.", volumeName.c_str());
    } else {
        INFOLOG("Delete volume %s successfully.", volumeName.c_str());
    }

    std::map<std::string, std::string> values = ToKevValue(resultvec);
    bool bCheck = values.find(VBS_TOOL_RET_CODE_KEY) != values.end();
    if (!bCheck) {
        ERRLOG("Create bitmap volume failed, retrun value error.");
        errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
        return DSWARE_API_ERR_UNKNOWN;
    }

    int returnCode = atoi(values[VBS_TOOL_RET_CODE_KEY].c_str());
    if (returnCode == DSWARE_API_OK || returnCode == DSWARE_API_ERR_VOLUME_IS_NOT_EXIST) {
        return DSWARE_API_OK;
    }
    errDes = GetErrString(returnCode);

    return returnCode;
}

int32_t FusionStorageApi::QuerySnapshot(const std::string &snapshotName, std::string &errDes)
{
    GetDswareAgentIp(m_fusionStorMgrIp);
    INFOLOG("Start to query snapshot %s dswareMgr IP: %s, dsAgent IP: %s.",
        snapshotName.c_str(), m_fusionStorMgrIp.c_str(), dsAgentIp.c_str());
    std::string agentHomedir = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    std::vector<Module::CmdParam> cmdParam {
        Module::CmdParam(Module::COMMON_CMD_NAME, "sudo"),
        Module::CmdParam(Module::SCRIPT_CMD_NAME, agentHomedir + SUDO_VBSCLI_PATH),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-c"),
        "querysnapshot",
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-s"),
        Module::CmdParam(Module::COMMON_PARAM, snapshotName),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-i"),
        Module::CmdParam(Module::COMMON_PARAM, m_fusionStorMgrIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "-p"),
        Module::CmdParam(Module::COMMON_PARAM, m_poolID)
    };
    std::vector<std::string> resultvec;
    if (Utils::CallAgentExecCmd(cmdParam, resultvec) != 0) {
        ERRLOG("Run querysnapshot command failed.");
        return FAILED;
    }
    std::map<std::string, std::string> values = ToKevValue(resultvec);
    bool bCheck = values.find(VBS_RET_CODE_KEY) != values.end();
    if (!bCheck) {
        ERRLOG("Query snapshot failed, retrun value error.");
        errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
        return DSWARE_API_ERR_UNKNOWN;
    }

    int returnCode = atoi(values[VBS_RET_CODE_KEY].c_str());
    if (returnCode == DSWARE_API_OK) {
        INFOLOG("Query snapshot %s success.", snapshotName.c_str());
        return DSWARE_API_OK;
    }
    errDes = GetErrString(returnCode);
    ERRLOG("Query snapshot %s failed, err: %s.", snapshotName.c_str(), errDes.c_str());
    return returnCode;
}

int32_t FusionStorageApi::QueryStoragePoolUsedRate(double &usedCapacityRate)
{
    GetDswareAgentIp(m_fusionStorMgrIp);
    INFOLOG("QueryStoragePoolUsedRate start dswareMgr IP: %s, dsAgent IP: %s, m_poolID: %s.",
        m_fusionStorMgrIp.c_str(), dsAgentIp.c_str(), m_poolID.c_str());
    std::string agentHomedir = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    std::vector<Module::CmdParam> cmdParam = {
        Module::CmdParam(Module::COMMON_CMD_NAME, "sudo"),
        Module::CmdParam(Module::SCRIPT_CMD_NAME, agentHomedir + SUDO_VBSTOOL_PATH),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--op"),
        "queryPoolInfo",
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--dsaIp"),
        Module::CmdParam(Module::COMMON_PARAM, dsAgentIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--dswareFloatIP"),
        Module::CmdParam(Module::COMMON_PARAM, m_fusionStorMgrIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--poolId"),
        Module::CmdParam(Module::COMMON_PARAM, m_poolID)
    };
    std::vector<std::string> resultvec;
    if (Utils::CallAgentExecCmd(cmdParam, resultvec) != 0) {
        ERRLOG("QueryStoragePoolUsedRate command failed.");
        return FAILED;
    }
    std::string errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
    std::map<std::string, std::string> values = ToKevValue(resultvec);
    if (values.find(VBS_TOOL_RET_CODE_KEY) == values.end()) {
        ERRLOG("QueryStoragePoolUsedRate failed, return value error: %s", errDes.c_str());
        return DSWARE_API_ERR_UNKNOWN;
    }
    int returnCode = atoi(values[VBS_TOOL_RET_CODE_KEY].c_str());
    if (returnCode != DSWARE_API_OK) {
        errDes = GetErrString(returnCode);
        ERRLOG("QueryStoragePoolUsedRate m_poolID:%s failed, err: %s.", m_poolID.c_str(), errDes.c_str());
        return returnCode;
    }
    auto noTotal = values.find(VBS_TOOL_TOTAL_CAPACITY_SIZE_KEY) == values.end();
    auto noUsed = values.find(VBS_TOOL_USED_CAPACITY_SIZE_KEY) == values.end();
    if (noTotal || noUsed) {
        ERRLOG("QueryStoragePoolUsedRate return value loss hasTotal: %d, hasUsed:%d", noTotal, noUsed);
        return DSWARE_API_ERR_UNKNOWN;
    }
    double totalCapacity = std::strtod(values[VBS_TOOL_TOTAL_CAPACITY_SIZE_KEY].c_str(), nullptr);
    double usedCapacity = std::strtod(values[VBS_TOOL_USED_CAPACITY_SIZE_KEY].c_str(), nullptr);
    INFOLOG("Get pool %s totalCapacity: %f ,usedCapacity: %f.", m_poolID.c_str(), totalCapacity, usedCapacity);
    usedCapacityRate = usedCapacity / totalCapacity;
    return SUCCESS;
}

int32_t FusionStorageApi::QueryVolumeAttach(const std::shared_ptr<RepositoryHandler> cacheRepoHandler,
    const std::string &filePath, const std::string &volName, std::vector<std::string> &ipList)
{
    std::string attachInfoFile = filePath + volName + "_volumeAttach.info";
    VolumeAttachInfo attachInfo;
    if (cacheRepoHandler == nullptr) {
        ERRLOG("Cache repohandle is null.");
        return FAILED;
    }
    std::string infoStr;
    if (Utils::ReadFile(cacheRepoHandler, attachInfoFile, infoStr) != SUCCESS) {
        WARNLOG("Read attach info file failed.");
        return FAILED;
    }
    Module::JsonHelper::JsonStringToStruct(infoStr, attachInfo);
    ipList = attachInfo.attachIpList;
    INFOLOG("Query volume %s attach info success.", volName.c_str());
    return SUCCESS;
}
 
int32_t FusionStorageApi::DetachVolumeByIp(const std::string &volName, const std::string &ip)
{
    GetDswareAgentIp(m_fusionStorMgrIp);
    INFOLOG("DetachVolumeByIp start dswareMgr IP: %s, dsAgent IP: %s, volName %s, detach ip %s, m_poolID: %s.",
        m_fusionStorMgrIp.c_str(), dsAgentIp.c_str(), volName.c_str(), ip.c_str(), m_poolID.c_str());
    std::string agentHomedir = Module::EnvVarManager::GetInstance()->GetAgentHomePath();
    std::vector<Module::CmdParam> cmdParam = {
        Module::CmdParam(Module::COMMON_CMD_NAME, "sudo"),
        Module::CmdParam(Module::SCRIPT_CMD_NAME, agentHomedir + SUDO_VBSTOOL_PATH),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--op"),
        "detachVolumeByIp",
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--dsaIp"),
        Module::CmdParam(Module::COMMON_PARAM, dsAgentIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--dswareFloatIP"),
        Module::CmdParam(Module::COMMON_PARAM, m_fusionStorMgrIp),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--detachIp"),
        Module::CmdParam(Module::COMMON_PARAM, ip),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--volName"),
        Module::CmdParam(Module::COMMON_PARAM, volName),
        Module::CmdParam(Module::CMD_OPTION_PARAM, "--poolId"),
        Module::CmdParam(Module::COMMON_PARAM, m_poolID)
    };
    std::vector<std::string> resultvec;
    if (Utils::CallAgentExecCmd(cmdParam, resultvec) != 0) {
        ERRLOG("DetachVolumeByIp command failed.");
        return FAILED;
    }
    std::string errDes = GetErrString(DSWARE_API_ERR_UNKNOWN);
    std::map<std::string, std::string> values = ToKevValue(resultvec);
    if (values.find(VBS_TOOL_RET_CODE_KEY) == values.end()) {
        ERRLOG("DetachVolumeByIp failed, return value error: %s", errDes.c_str());
        return DSWARE_API_ERR_UNKNOWN;
    }
    int returnCode = atoi(values[VBS_TOOL_RET_CODE_KEY].c_str());
    if (returnCode != DSWARE_API_OK) {
        errDes = GetErrString(returnCode);
        ERRLOG("DetachVolumeByIp m_poolID:%s failed, err: %s.", m_poolID.c_str(), errDes.c_str());
        return returnCode;
    }
    INFOLOG("Detach volume %s from %s success.", volName.c_str(), ip.c_str());
    return SUCCESS;
}
 
int32_t FusionStorageApi::DetachVolumeAllAttach(const std::shared_ptr<RepositoryHandler> cacheRepoHandler,
    const std::string &filePath, const std::string &volName)
{
    std::vector<std::string> ipList;
    if (QueryVolumeAttach(cacheRepoHandler, filePath, volName, ipList) != DSWARE_API_OK) {
        ERRLOG("Query volume %s attach failed.", volName.c_str());
        return FAILED;
    }
    int32_t ret = SUCCESS;
    for (const auto &ip: ipList) {
        if (DetachVolumeByIp(volName, ip) != SUCCESS) {
            ERRLOG("DetachVolumeByIp failed, volname %s, ip %s.", volName.c_str(), ip.c_str());
            ret = FAILED;
        }
    }
    return ret;
}

std::string FusionStorageApi::VectorToStr(std::vector<std::string> &vecStr)
{
    std::string stderroutput;
    for (const auto &it : vecStr) {
        stderroutput += it + " ";
    }

    return stderroutput;
}

VIRT_PLUGIN_NAMESPACE_END
